######################################################################
# Time-stamp: <05/06/16 12:38:53 ostolop>
######################################################################
package EP::EPC::Core::Pattern_Matching;

use strict;

use EP::Config;
use EP::Common;
use EP::Common::General;
use EP::Common::Error qw/:try/;

use EP::EPC::Core;
use EP::EPC::Core::Component;

use EP::Core::Data::Parser::seq;
use EP::Core::Dataset::Data::seq;

use CGI qw/:standard/;

use Carp;
use Carp qw (cluck);

use Bio::DB::GFF;
use Bio::Graphics;
#use GD::SVG;
use Bio::SeqFeature::Generic;

use File::Temp;
use File::Basename;

our @ISA = qw ( EP::EPC::Core::Component );

# sub: process_component
#   Processes the Pattern_Matching component.
#   It expects the query parameters upstream_bp and patterns (a list of patterns) to be set.
# returns:
# OK if everything went fine
sub process_component {
  my $self = shift;

  my $result = $self->_visualize_patterns ();
  my $process_result = { patterns => $result,
			 src_dataset_id => $self->{src_dataset}->id,
			 dst_dataset_id => $self->{src_dataset}->id
		       }; # HACK: produce HTML output and place it all directly in <html></html> as a result!

  $self->{CD}->{process_result} = $process_result;
  return $self->record_analysis_history ();
}

# sub: _visualize_patterns
#   Routine that retrieves the sequencees, preproccesses the patterns and calls the 
#   _run_visualization routine. It expects the query parameters upstream_bp and 
#   patterns (a list of patterns) to be set.
# returns:
#   html formatted visualization results
sub _visualize_patterns {
  my $self = shift;

  throw EP::Common::Error ( -value => AFFY_TRANSFORM_RAW_DATA ) if $self->{src_dataset}->type eq "affy";

  my $dataset = $self->{src_dataset};

  my $dst_dataset;

  # Create the sequence dataset object out of this dataset if it's an exp dataset
  if ( $dataset->type eq "exp" ) {
    $self->{session}->status( "Retrieving sequences ..." );
    my @ids = map { $dataset->data->rows->{$_}{full_annot}->Values(0) } sort { $a <=> $b } keys %{ $dataset->data->rows };
    die "No genes found for dataset nr $dataset" if scalar(@ids) == 0;

    my $seq = EP::Core::Data::Parser::seq->_get_sequences( $dataset->species, \@ids, $self->{query}->param('upstream_bp') );
    $dst_dataset = $dataset->copy ( { name => undef } );
    $dst_dataset->type ( 'seq' );

    my $data = new EP::Core::Dataset::Data::seq ( sequences => $seq );

    $dst_dataset->data ( $data );
#    $dst_dataset->data->start_bp( -1 * $self->{query}->param('upstream_bp') );
#    $dst_dataset->data->end_bp( 0 );
  
    EP::Core::Data::Parser::seq->_write_dataset_data ( $dst_dataset, $data );
    $dst_dataset->data->process_metadata ();
  } else {
    $dst_dataset = $dataset;
  }

  # Visualize results
  $self->{session}->status( "Visualizing patterns ..." );
  my (@patterns) = split /\s+/, $self->{query}->param('patterns');

  my $patterns_dataset;

  my @pnames = $self->{query}->param;
  my @meta_ids = map { /chk_meta_(\d+)/ } grep { /^chk_meta/ } @pnames;

  if ( scalar @meta_ids == 1 ) {
    my $spexs_meta = EP::Common::DBI::Action_Metadata->retrieve ( $meta_ids[0] )->value;

    use XML::LibXML;
    my $xml = new XML::LibXML;
    my $doc = $xml->parse_string ( `GET $spexs_meta` );

    my @p = $doc->findnodes ( '//row/data[1]' );
    my @pp = map { $_->textContent } @p;
    push @patterns, @pp;
  }

  die "No patterns entered" if scalar(@patterns) == 0;

  my $output_file = $self->_run_visualization ( $dst_dataset, \@patterns );

  return {  type  => "image/png",
	    value => $EP::Config::h->{HTML_HREF} . "/tmp/" . $output_file,
	    label => "Visualized patterns" };
}

# sub: _run_visualization
#   Visualization routine. Takes the sequences from a dataset object and visualizes
#   a certain number of patterns on those.
# parameters:
#   dataset - Which dataset to get the sequences from
#   patterns - What patterns to visualize
# returns:
#   filename of the picture generated
sub _run_visualization {
  my $self = shift;
  my ($dataset, $patterns) = @_;
  my $nr_seq = $dataset->data->{nr_sequences};

  my (@genes_notfound, $spacing, $key_style, $bgcolor, $pad_left, $pad_bottom, $track_height, $glyph_type, $glyph_color);

  warn "Drawing $nr_seq sequences...";

  # At what number of sequences to switch display mode
  # Need to think about this... doesn't look to nice yet
  if ( $nr_seq <= 300 ) {
      $spacing = 4;
      $key_style = "left";
      $bgcolor = "white";
      $pad_left = 100;
      $pad_bottom = 10;
      $track_height = 8;
      $glyph_type = "box";
      $glyph_color = "-bgcolor";
  } else {
      $spacing = 1;
      $key_style = "bottom";
      $bgcolor = "black";
      $pad_left = 0;
      $pad_bottom = 0;
      $track_height = 1;
      $glyph_type = "line";
      $glyph_color = "-fgcolor";
  }

  # Panel to draw the sequences on
  my $panel = Bio::Graphics::Panel->new(
					-length => $dataset->data->bp_length(),
#					-image_class => "GD::SVG",
					-key_style => $key_style,
					-width => 1024,
					-pad_left => $pad_left,
					-pad_bottom => $pad_bottom,
					-pad_top => 6 * scalar @$patterns + 10,
					-spacing => $spacing,
					-bgcolor => $bgcolor,
				       );

  # Not very nice yet... this just gets the colors that are supported by the panel object
  my @colors = Bio::Graphics::Panel->color_names;

  # Get all sequences from the Bio::seqIO stream
  # We need to catch the errors however, otherwise the program will die here
  my $seq_count = 1;
  my $stream = $dataset->data->sequences;

  ### UGlY HACK ###
  # the get_seq_stream and get_Stream_by_name do no actually return the same objects
  # so we can only get the id if it is coming from get_Stream_by_name
  my $try_id = $dataset->data->sequences->{ids}->[0] if ref ( $dataset->data->sequences ) eq 'Bio::DB::GFF::ID_Iterator';

  while ( my $gene = $stream->next_seq() ) {
    my $upstream = $gene->subseq ( $dataset->data->start_bp(), $dataset->data->end_bp() ) if $dataset->data->start_bp();
    $upstream = $gene if not $dataset->data->start_bp();
    my $seq = $upstream->seq() if $upstream;

    if ( $upstream ) {
      my $feature = Bio::SeqFeature::Generic->new ( -start => 1,
						    -end => length ( $seq ) );

      $feature->set_attributes ( -tag => { "upstream_seq" => 1 } );

      my (@glyphs);
      push @glyphs, $feature;

      foreach my $idx ( 0 .. $#{$patterns} ) {
	my $pattern_length = length ( $$patterns[$idx] );
	
	while ( $seq =~ /$$patterns[$idx]/g ) {
	  my $pattern_start = length($`);
	  my $pattern = Bio::SeqFeature::Generic->new ( -start => $pattern_start + 1,
							-end => $pattern_start + $pattern_length
						      );
	  $pattern->set_attributes( -tag => { color => $colors[$idx] } );
	  push @glyphs, $pattern;
	}
      }

      $panel->add_track( \@glyphs,
			 -glyph => sub { my $feature = shift;
					 $feature->has_tag("upstream_seq") ? 'arrow' : $glyph_type; },
			 $glyph_color => sub { my $feature = shift;
					       my (@values) = $feature->get_tag_values("color");
					       $values[0];
					     },
			 -key  => $gene->display_id(),
			 -height => $track_height,
			 -bump => 0,
			 -bump_limit => 3
		       );

      $self->{session}->status( "Finished sequence $seq_count" ) if $seq_count/100 == int($seq_count/100);
      $seq_count++;
    }
  }

  warn "The sequences for the following genes where not obtained: " . join ( " ", @genes_notfound ) if scalar(@genes_notfound) > 0;

  use GD;
  my $gd = $panel->gd;

  foreach my $idx ( 0 .. $#{$patterns} ) {
    my $color = $colors[$idx];
    my $pattern = $$patterns[$idx];
    my $coloridx = $panel->translate_color($color);

    $gd->string(gdSmallFont,10,10 + $idx * 10,$pattern,$coloridx);
  }

  my $file = new File::Temp ( DIR => "$EP::Config::h->{TMP}", UNLINK => 0, SUFFIX => ".png" );
  print $file $gd->png;

  my ($cln_file) = basename ( $file );
  return $cln_file;
}

1;
